#include <iostream>
#include <sstream>
#include <cstring>
#include "stun_message.h"

using namespace std;

StunMessage::StunMessage(){

}

StunMessage::StunMessage(const StunMessage& rhs)
    : msg_header_(rhs.msg_header_)
    ,attributes_(rhs.attributes_)
{

}
StunMessage& StunMessage::operator=(const StunMessage& rhs){
    msg_header_ = rhs.msg_header_;
    attributes_ = rhs.attributes_;
    return *this;
}

StunMessage::~StunMessage() {
}

uint32_t StunMessage::stun_encode_message(char *buf, uint16_t bufLen) {
    return 0;
}

bool StunMessage::stun_parse_message(char *buffer, uint16_t length) {

    if (length < StunMessageHeader::StunHeaderLength)
    {
        return false;
    }
    msg_header_.parse(buffer, StunMessageHeader::StunHeaderLength);

    // parse body
    char* body = buffer + StunMessageHeader::StunHeaderLength;
    uint16_t size = msg_header_.get_msg_length();

    uint16_t attr_len = 0;
    uint16_t attr_type = 0;

    while(size > 0)
    {
        memset(&attr_type, 0, 2);
        memcpy(&attr_type, body, 2);
        attr_type = ntoh16(attr_type);

        memset(&attr_len, 0, 2);
        memcpy(&attr_len, body+2, 2);
        attr_len = ntoh16(attr_len);
        // attrLen may not be on 4 byte boundary, in which case we need to pad to 4 bytes when advancing to next attribute

        uint16_t attr_len_pad = attr_len % 4 == 0 ? 0 : 4 - (attr_len % 4);

        body += 4; // skip the length and type in attribute header
        size -= 4;
        switch ( attr_type )
        {
            case MappedAddress:
                 if ( stun_parse_atr_address(body, attr_len, mapped_address_) == false )
                 {
                     std::cout << "problem parsing XorMappedAddress" << std::endl;
                     return false;
                 }
                 break;
            case XorMappedAddress:
                 if ( stun_parse_atr_xor_address(body, attr_len, xor_mapped_address_) == false )
                 {
                    std::cout << "problem parsing XorMappedAddress" << std::endl;
                    return false;
                 }
                // std::cout << "xor_mapped_address_: "<< dec<< xor_mapped_address_.port_ << std::endl;
                // std::cout << "xor_mapped_address_: "<< hex<< xor_mapped_address_.addr_.ipv4_ << std::endl;
                 break;
            default:break;
        }

        body += (attr_len + attr_len_pad);
        size -= (attr_len + attr_len_pad);
    }

    return true;
}

bool StunMessage::stun_parse_atr_xor_address( char* body, uint16_t buffer_len, StunAtrAddress& result)
{
    bool ret = stun_parse_atr_address(body, buffer_len, result);
    if(ret)
    {
        apply_xor_to_address(result, result);
    }
    return ret;
}

bool StunMessage::stun_parse_atr_address( char* body, uint16_t hdr_len, StunAtrAddress& result)
{
    if ( hdr_len != 8 /* ipv4 size */ && hdr_len != 20 /* ipv6 size */ )
    {
        std::cout << "hdr_len wrong for Address: " << std::endl;
        return false;
    }
    body++;  // Skip pad
    result.family_ = *body++;

    uint16_t nport = 0;
    memcpy(&nport, body, 2);
    body += 2;
    result.port_ = ntoh16(nport);

    if (result.family_ == IPv4Family)
    {
        uint32_t naddr = 0;
        memcpy(&naddr, body, sizeof(uint32_t));
        body += sizeof(uint32_t);
        result.addr_.ipv4_ = ntoh32(naddr);
        // Note:  addr.ipv4 is stored in host byte order
        //std::cout << "result.addr_.ipv4_: " << result.addr_.ipv4_ << std::endl;
        return true;
    }
    else if (result.family_ == IPv6Family)
    {
        memcpy(&result.addr_.ipv6_, body, sizeof(result.addr_.ipv6_));
        body += sizeof(result.addr_.ipv6_);
        // Note:  addr.ipv6 is stored in host byte order
        return true;
    }
    else
    {
        std::cout << "bad address family: " << result.family_;
    }

    return false;
}

void StunMessage::apply_xor_to_address(const StunAtrAddress& input, StunAtrAddress& output)
{
    if(&input != &output) {
        memcpy(&output, &input, sizeof(output));
    }

    output.port_ = output.port_^(StunMessageHeader::StunMagicCookie>>16); // Xor with most significate 16 bits of magic cookie
    if(output.family_ == IPv6Family)
    {
        for(int i = 0; i < 4; i++)
        {
            // Note:  MagicCookieAndTid are stored in network byte order
            //output.addr_.ipv6_.longpart[i] = output.addr_.ipv6_.longpart[i]^mHeader.magicCookieAndTid.longpart[i];
        }
    }
    else
    {
        output.addr_.ipv4_ = output.addr_.ipv4_ ^StunMessageHeader::StunMagicCookie;
    }
}

uint16_t StunMessage::get_reflexive_port() const{
    return xor_mapped_address_.port_;
}

std::string StunMessage::get_reflexive_address() const{
    stringstream ss;
    ss << ((xor_mapped_address_.addr_.ipv4_ >> 24) & 0xFF)  << "."
       << ((xor_mapped_address_.addr_.ipv4_ >> 16) & 0xFF)  << "."
       << ((xor_mapped_address_.addr_.ipv4_ >> 8) & 0xFF)  << "."
       << (xor_mapped_address_.addr_.ipv4_ & 0xFF) ;
    return ss.str();
}


